
package com.jf.cloud.search.manager;

import com.jf.cloud.api.dto.EsPageDTO;
import com.jf.cloud.api.vo.EsPageVO;
import com.jf.cloud.common.exception.LuckException;
import com.jf.cloud.common.order.dto.OrderInvoiceSearchDTO;
import com.jf.cloud.common.order.vo.EsOrderInvoiceVO;
import com.jf.cloud.common.util.Json;
import com.jf.cloud.search.constant.EsIndexEnum;
import ma.glasnost.orika.MapperFacade;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author zz
 * @date 2020/11/16
 */
@Component
public class OrderInvoiceSearchManager {

    private static final Logger log = LoggerFactory.getLogger(OrderInvoiceSearchManager.class);

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private MapperFacade mapperFacade;

    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderInvoiceSearchDTO 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsOrderInvoiceVO> page(EsPageDTO pageDTO, OrderInvoiceSearchDTO orderInvoiceSearchDTO) {
        orderInvoiceSearchDTO.setPageSize(pageDTO.getPageSize());
        orderInvoiceSearchDTO.setPageNum(pageDTO.getPageNum());
        EsPageVO<EsOrderInvoiceVO> page = pageSearchResult(orderInvoiceSearchDTO);
        return page;
    }


    /**
     * 通过搜索信息分页搜索es数据的信息
     *
     * @param orderInvoiceSearchDTO 退款订单搜索条件
     * @return 搜索结果
     */
    public EsPageVO<EsOrderInvoiceVO> pageSearchResult(OrderInvoiceSearchDTO orderInvoiceSearchDTO) {
        if (Objects.isNull(orderInvoiceSearchDTO.getPageSize())) {
            orderInvoiceSearchDTO.setPageSize(10);
        }
        if (Objects.isNull(orderInvoiceSearchDTO.getPageNum())) {
            orderInvoiceSearchDTO.setPageNum(1);
        }
        //1、查询订单数据
        SearchResponse response = searchResult(orderInvoiceSearchDTO);
        //2.处理响应的数据
        return pageEsOrderBO(response, orderInvoiceSearchDTO.getPageSize());
    }

    private EsPageVO<EsOrderInvoiceVO> pageEsOrderBO(SearchResponse response, Integer pageSize) {
        EsPageVO<EsOrderInvoiceVO> pageVO = new EsPageVO<>();
        List<EsOrderInvoiceVO> orderInvoiceList = getOrderInvoiceListByResponse(response.getHits().getHits());
        pageVO.setList(orderInvoiceList);
        buildSearchPage(pageVO, response, pageSize);
        return pageVO;
    }

    /**
     * 构建分页数据
     * @param esPageVO
     * @param response
     * @param pageSize
     */
    private void buildSearchPage(EsPageVO<?> esPageVO, SearchResponse response, Integer pageSize) {
        //总记录数
        long total = response.getHits().getTotalHits().value;
        esPageVO.setTotal(total);
        // 总页码
        int totalPages = (int)total % pageSize == 0 ?
                (int)total / pageSize : ((int)total / pageSize + 1);
        esPageVO.setPages(totalPages);
    }

    private SearchResponse searchResult(OrderInvoiceSearchDTO orderInvoiceSearchDTO) {
        // 封装检索条件
        SearchRequest searchRequest = buildSearchRequest(orderInvoiceSearchDTO);
        SearchResponse response;
        try {
            //2、执行检索请求
            response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            log.info("搜索返回结果：" + response.toString());

        } catch (IOException e) {
            log.error(e.toString());
            throw new LuckException("搜索服务出了点小差，请稍后再试", e);
        }
        return response;
    }

    /**
     * 从es返回的数据中获取spu列表
     *
     * @param hits es返回的数据
     * @return
     */
    private List<EsOrderInvoiceVO> getOrderInvoiceListByResponse(SearchHit[] hits) {
        List<EsOrderInvoiceVO> orderInvoiceList = new ArrayList<>();
        for (SearchHit hit : hits) {
            EsOrderInvoiceVO esOrderInvoiceVO = Json.parseObject(hit.getSourceAsString(), EsOrderInvoiceVO.class);
            orderInvoiceList.add(esOrderInvoiceVO);
        }
        return orderInvoiceList;
    }


    /**
     * 准备检索请求
     *
     * @param param   搜索参数
     * @return
     */
    private SearchRequest buildSearchRequest(OrderInvoiceSearchDTO param) {

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建bool-query
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();

        // 过滤
        filterQueryIfNecessary(param, boolQueryBuilder);

        // 排序
        sort(searchSourceBuilder, boolQueryBuilder);

        if (Objects.nonNull(param.getPageNum()) && Objects.nonNull(param.getPageSize())) {
            //分页
            searchSourceBuilder.from((param.getPageNum()-1) * param.getPageSize());
            searchSourceBuilder.size(param.getPageSize());
        }

        log.info("构建的DSL语句 {}", searchSourceBuilder.toString());

        return new SearchRequest(new String[]{EsIndexEnum.ORDER_INVOICE.value()}, searchSourceBuilder);
    }


    /**
     * 进行排序
     */
    private void sort(SearchSourceBuilder searchSourceBuilder, BoolQueryBuilder boolQueryBuilder) {
        searchSourceBuilder.sort("applicationTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);
    }

    /**
     * 过滤查询条件，如果有必要的话
     *
     * @param param            查询条件
     * @param boolQueryBuilder 组合进boolQueryBuilder
     */
    private void filterQueryIfNecessary(OrderInvoiceSearchDTO param, BoolQueryBuilder boolQueryBuilder) {
        // 店铺id
        if (Objects.nonNull(param.getShopId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("shopId", param.getShopId()));
        }
        // 订单id
        if (Objects.nonNull(param.getOrderId())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("orderId", param.getOrderId()));
        }
        // 发票状态
        if (Objects.nonNull(param.getInvoiceState())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("invoiceState", param.getInvoiceState()));
        }
    }
}
